<?php

/**
 * DefaultIoCConfigHandler 
 * 
 * @uses sfYamlConfigHandler
 * @package plugins.sfIocPlugin
 * @version $id$
 * @copyright 2008
 * @author Marcin Nowak <marcin.j.nowak@gmail.com> 
 * @license LGPL
 */
class DefaultIoCConfigHandler extends sfYamlConfigHandler
{
    public function execute($configFiles)
    {
        // parse the yaml
        $myConfig = $this->parseYamls($configFiles);

        $myConfig = sfToolkit::arrayDeepMerge(
            isset($myConfig['default']) && is_array($myConfig['default']) ? $myConfig['default'] : array(),
            isset($myConfig['all']) && is_array($myConfig['all']) ? $myConfig['all'] : array(),
            isset($myConfig[sfConfig::get('sf_environment')]) && is_array($myConfig[sfConfig::get('sf_environment')]) ? $myConfig[sfConfig::get('sf_environment')] : array()
        );

        $_render_construct_params = array();

        try
        {
        foreach($myConfig as $bean_id=>$definition)
        {
            /* @fixme: handle namespaces for php5.3 */
            $class_name = basename(str_replace('.','/',$definition['class']));

            if(!array_key_exists('singleton',$definition))
            {
                $classInfo = new sfAnnotatedClass($class_name);
                $constructorInfo = $classInfo->getConstructor();
                $definition['singleton'] = false;
                $reflectionClass = $classInfo;
                while($reflectionClass)
                {
                    if($reflectionClass->hasAnnotation('Singleton')) { $definition['singleton'] = true; break; }
                    if($reflectionClass->hasAnnotation('Prototype')) { $definition['singleton'] = false; break; }
                    $reflectionClass = $reflectionClass->getParentClass();
                }
                $myConfig[$bean_id]['singleton'] = $definition['singleton'];
            }

            if(!isset($definition['construct_params']))
            {
                if(!isset($classInfo))
                {
                    $classInfo = new sfAnnotatedClass($class_name);
                    $constructorInfo = $classInfo->getConstructor();
                }

                if($constructorInfo && $constructorInfo->hasAnnotation('Inject'))
                {
                    $bind_annotations = $constructorInfo->getAllAnnotations('Bind');
                    $value_annotations = $constructorInfo->getAllAnnotations('Value');

                    $construct_parameters = array();
                    $constructorParameters = $constructorInfo->getParameters();
                    foreach($constructorParameters as $parameter)
                    {
                        $parameterClass = $parameter->getClass();
                        if($parameterClass)
                        {
                            $ref_id = $parameterClass->getName();
                            foreach($bind_annotations as $annotation)
                            {
                                if($annotation->name == $parameter->getName())
                                {
                                    $ref_id = $annotation->to;
                                    break;
                                }
                            }
                            //$construct_parameters[] = array('ref'=>$ref_id, 'class'=>$parameterClass->getName());
                            $construct_parameters[] = $ref_id.'&';
                        }
                        else if($parameter->isArray())
                        {
                            throw new sfConfigurationException('array parameters are not supported yet');
                        }
                        else
                        {
                            $value = null;
                            foreach($value_annotations as $annotation)
                            {
                                if($annotation->name == $parameter->getName())
                                {
                                    $value = $annotation->to;
                                    break;
                                }
                            }
                            $construct_parameters[] = $value;
                        }
                    }
                    $myConfig[$bean_id]['construct_params'] = $construct_parameters;
                }
                else
                {
                    $myConfig[$bean_id]['construct_params'] = null;
                }
            }
            $params = var_export($myConfig[$bean_id]['construct_params'], true);
            $params = preg_replace("/('([^']+)&')/", "new BeanDefinitionReference('\\2')", $params);
            $_render_construct_params[$bean_id] = $params;
            $myConfig[$bean_id]['construct_params'] = '###'.$bean_id.'_CONSTRUCT_PARAMS###';
        }
        }
        catch (ReflectionException $e)
        {
            throw new sfConfigurationException(sprintf('Cannot create bean "%s" definition. Cannot read %s class adnotations [%s]', $bean_id, $class_name, $e->getMessage()));
        }


        $factory = sfInflector::camelize($this->getParameterHolder()->get('factory','default'));
        $parent = sfInflector::camelize($this->getParameterHolder()->get('parent',''));

        if($parent)
        {
        $retval = sprintf("sfContext::getInstance()->set('%sBeanFactory', new ArrayBeanFactory( %s, %s )); \n", 
            $factory,
            var_export($myConfig, true),
            'sfContext::getInstance()->get(\''.$parent.'BeanFactory\')'
        );
        }
        else
        {
        $retval = sprintf("sfContext::getInstance()->set('%sBeanFactory', new ArrayBeanFactory( %s )); \n", 
            $factory,
            var_export($myConfig, true));
        }

        if(preg_match_all("/'###(.+)_CONSTRUCT_PARAMS###'/", $retval, $matches))
        {
            foreach($matches[0] as $k=>$src)
            {
                $retval = preg_replace('/'.$src.'/', $_render_construct_params[$matches[1][$k]], $retval);
            }
        }

        return sprintf("<?php\n".
            "// auto-generated by DefaultBeanFactoryConfigHandler\n".
            "// date: %s\n%s",
            date('Y/m/d H:i:s'), $retval);
    }
}
